/****************************************************************************
 * apleilx
 * 22.0727
 ****************************************************************************/

#ifndef _QC_COM_MOUDLE_
#define _QC_COM_MOUDLE_

#include "mytype.h"
#include "dat_quene.h"
/*****************************************************************************
 * @brief  CONFIG
 *****************************************************************************/
#define QC_BUFF_SIZE 256

/*****************************************************************************
 * @brief  工作模式
 *****************************************************************************/
#define QC_MODE_SLAVE 0
#define QC_MODE_MASTER 1

/*****************************************************************************
 * @brief   QC ID 配置
 *****************************************************************************/
#define QC_SLAVE_ID_MSK 0x0FU
#define QC_ZONE_POS 4U
#define QC_ZONE_MSK 0xF0U

/*****************************************************************************
 * @brief   硬件状态
 *****************************************************************************/
typedef enum
{
	QC_RX_IDLE = 0,		 // 空闲
	QC_RX_RCV = 1,		 // 活动计时状态
	QC_RX_END = 2,		 // 任务处理状态，接收完成
	QC_RX_TOUT = 3,		 // 任务处理状态，等待超时
	QC_RX_WAIT = 4,		 // 活动计时状态，主机发完进入等待
	QC_RX_NACK_WAIT = 5, // 活动计时状态，主机发完进入无响应等待
	QC_RX_NACK_END = 6,	 // 任务处理状态，主机NACK结束
} qc_rx_type;

typedef enum
{
	QC_TX_IDLE = 0, // 空闲
	QC_TX_BUSY = 1, // 发送状态
} qc_tx_type;

/*****************************************************************************
 * @brief   QC 从机回调
 *****************************************************************************/
typedef const struct
{
	void (*dat_set)(uu16 addr, uu16 dat); // addr 高8位为page
	uu16 (*dat_get)(uu16 addr);			  // addr 高8位为page
} qc_com_slave_type;

/*****************************************************************************
 * @brief   MODBUS 从机回调
 *****************************************************************************/
typedef const struct
{
	void (*hold_dat_set)(uu16 addr, uu16 dat);
	uu16 (*hold_dat_get)(uu16 addr);
	uu16 (*input_dat_get)(uu16 addr);
	void (*coil_dat_set)(uu16 Bindex, uu8 dat);
	uu8 (*coil_dat_get)(uu16 Bindex);
	uu8 (*disc_dat_get)(uu16 Bindex);
} qc_mb_slave_type;

/*****************************************************************************
 * @brief   MODBUS 指令区域及写指令分发方式
 *****************************************************************************/
#define QC_MB_ATTR_HOLD_SW (1) // REG单写（HOLDING 区）
#define QC_MB_ATTR_HOLD_MW (2) // REG多写（HOLDING 区）
#define QC_MB_ATTR_HOLD_RW (3) // REG读写（HOLDING 区）
#define QC_MB_ATTR_INPUT (4)   // 04指令区（INPUT 区）
#define QC_MB_ATTR_COIL_SW (5) // IO单写（COILS 区）
#define QC_MB_ATTR_COIL_MW (6) // IO多写（COILS 区）
#define QC_MB_ATTR_DISC (7)	   // 离散区（DISCRETE 区）

/*****************************************************************************
 * @brief   VGUS
 *****************************************************************************/
#define QC_GUS_ATTR_PV (1)
#define QC_GUS_ATTR_REG (2)
#define QC_GUS_ATTR_LINE (3)
#define QC_GUS_ATTR_AUTO (4)

/*****************************************************************************
 * @brief   cmd struct
 *****************************************************************************/
typedef struct
{
	uu8 id;		 // 0 代表广播
	uu8 wn, rn;	 // 0 - 120
	uu8 attr;	 // 命令特征，for MB。
	uu16 wa, ra; // wa 高8位指示页面
	uu16 *rdat;
	uu16 *wdat;
	void (*callback)(uu8 site, uu8 err);
} qc_cmd_type;

/*****************************************************************************
 * @brief   API
 *****************************************************************************/
typedef struct
{
	void (*obj_init)(qc_cmd_type *clist, uu8 cnum, uu8 stcnum);
	void (*cmd_clr)(void);
	uint8_t (*dyna_cmd_req)(qc_cmd_type *cmd);
	uint8_t (*stc_cmd_req)(uint8_t cmd_site, qc_cmd_type *cmd);
	uint8_t (*stc_cmd_del)(uint8_t cmd_site);
	uint8_t (*cmd_ready_num)(void);
	uint8_t (*cmd_idle_num)(void);
} qcom_api_type;

/*****************************************************************************
 * @brief   qc object
 *****************************************************************************/
typedef struct
{
	qc_rx_type rx_st;			  // 收发状态
	qc_tx_type tx_st;			  // 发送状态
	uu8 mode;					  // 【Y】主从模式
	uu8 err_hal;				  //  hal 错误
	uu8 et_sv;					  // 【Y】主命令间隔
	uu8 slave_id;				  // 【Y】从机地址
	uu8 rid;					  // 从机接收到的ID
	uu8 tim_sv, tout_sv;		  // 【Y】断言时间, QC_RX_RCV状态计时，超时判断QC_RX_TERR。接收字符清除计数。
	uu8 tim_cnt, tout_cnt;		  // 超时时间, QC_RX_WAIT状态计时，超时判断QC_RX_TOUT。
	uu8 nack_tim_sv;			  // 【Y】主模式0号地址非应答，延时时间为 tim_sv + nack_tim_sv
	uu8 nack_tim_cnt;			  // nack 状态计时
	uu8 tx_cnt, tx_size;		  // 发送计数
	uu8 rx_cnt;					  // 接收计数
	uu16 idle_tim_cnt;			  //
	uu16 act_tim_cnt;			  //
	uu16 success_cnt, fail_cnt;	  // 执行结果计数
	uu16 success_keep, fail_keep; // 执行结果连续计数
	uu16 sucess_none_time_cnt;

	struct
	{
		com_quene_type dcmd_quene;
		qc_cmd_type *cmd_list;
		uu8 st_cmd_empty;
		uu8 stc_cmd_site;
		uu8 stc_cmd_size;
		uu8 cmd_exe_site;
		uu8 cmd_size;
		uu8 err_cnt, err_sv;
		uu8 cmd_split_cnt; //内部管理
		uu8 fun_code;	   //功能码
		qc_cmd_type cmd_now;
	} master;								  //【Y】
	void const *slave;						  //【Y】
											  //
	void (*os_event_send)(uu16 event);		  //【Y】
	void (*dat_send)(void);					  //【Y】
	void (*proc_callback)(uu8 site, uu8 err); //【Y】对于从机，site 固定为0
	const qcom_api_type *fun;

	uu8 buff[QC_BUFF_SIZE];
} qc_obj_type;

/*****************************************************************************
 * @brief   slave 默认指向内部定义接口，接口为弱定义，内容如下
 *****************************************************************************
 * OBJ_WEAK void qc_hold_dat_set(uu16 addr, uu16 dat){};
 * OBJ_WEAK uu16 qc_hold_dat_get(uu16 addr) { return 0; }
 * OBJ_WEAK uu16 qc_input_dat_get(uu16 addr) { return 0; }
 * OBJ_WEAK void qc_coil_dat_set(uu16 Bindex, uu8 dat){};
 * OBJ_WEAK uu8 qc_coil_dat_get(uu16 Bindex) { return 0; }
 * OBJ_WEAK uu8 qc_disc_dat_get(uu16 Bindex) { return 0; }
 * OBJ_WEAK qc_mb_slave_type qc_mb_slave = {
 * 	qc_hold_dat_set,
 * 	qc_hold_dat_get,
 * 	qc_input_dat_get,
 * 	qc_coil_dat_set,
 * 	qc_coil_dat_get,
 * 	qc_disc_dat_get,
 * };
 *****************************************************************************/

/*****************************************************************************
 * @brief   QC ERROR DEFINE
 *****************************************************************************/
#define QC_ERR_NONE 0
#define QC_ERR_RN 1
#define QC_ERR_WN 2
#define QC_ERR_RXCNT 3

#define QC_ERR_CRC 0x11
#define QC_ERR_RID 0x12
#define QC_ERR_TOUT 0x13

/*****************************************************************************
 * @brief   QC API
 *****************************************************************************/
void mqc_poll(qc_obj_type *obj);
void sqc_poll(qc_obj_type *obj);
void qc_poll(qc_obj_type *obj);

/*****************************************************************************
 * @brief  MODBUS API
 *****************************************************************************/
void sqc_mb_poll(qc_obj_type *obj);
void mqc_mb_poll(qc_obj_type *obj);
void qc_mb_poll(qc_obj_type *obj);

/*****************************************************************************
 * @brief  VGUS API
 *****************************************************************************/
#define VGUS_RGE_LED 0x01
#define VGUS_RGE_BUZZER 0x02
#define VGUS_RGE_PAGE 0x03
#define VGUS_RGE_RUN_TIME 0x0C
#define VGUS_RGE_RTC_ADJ 0x1F
#define VGUS_RGE_RTC_NOW 0x20
#define VGUS_REG_MUSIC_PLAY 0x50
#define VGUS_REG_MUSIC_NUM 0x51
#define VGUS_REG_VOICE_ADJ 0x53
#define VGUS_REG_VOICE_VAL 0x54
#define VGUS_REG_AVI 0x60
void qc_vgus_poll(qc_obj_type *obj);
void qc_vgus_page_set(qc_obj_type *obj, uint16_t *page, void (*callback)(uint8_t, uint8_t));
void qc_vgus_pv_read(qc_obj_type *obj, uint16_t *dat, uint16_t addr, uint16_t len, void (*callback)(uint8_t, uint8_t));
void qc_vgus_pv_write(qc_obj_type *obj, uint16_t *dat, uint16_t addr, uint16_t len, void (*callback)(uint8_t, uint8_t));

/*****************************************************************************
 * @brief  DGUS API
 *****************************************************************************/
void qc_dgusii_poll(qc_obj_type *obj);
void qc_dgus_poll(qc_obj_type *obj);

/*****************************************************************************
 * @brief   OBJ & CMD
 *****************************************************************************/
void qc_obj_init(qc_obj_type *obj, qc_cmd_type *clist, uu8 cnum, uu8 stcnum);
void mqc_cmd_next(qc_obj_type *obj);
void mqc_cmd_clr(qc_obj_type *obj);
uint8_t mqc_dyna_cmd_req(qc_obj_type *obj, qc_cmd_type *cmd);
uint8_t mqc_stc_cmd_req(qc_obj_type *obj, uint8_t cmd_site, qc_cmd_type *cmd);
uint8_t mqc_stc_cmd_del(qc_obj_type *obj, uint8_t cmd_site);
uint8_t mqc_cmd_ready_num(qc_obj_type *obj);
uint8_t mqc_cmd_idle_num(qc_obj_type *obj);
void qc_exe_success(qc_obj_type *obj);
void qc_exe_fail(qc_obj_type *obj);

#define qcom_api_make(name, qobj)                                         \
	static void name##_obj_init(qc_cmd_type *clist, uu8 cnum, uu8 stcnum) \
	{                                                                     \
		qc_obj_init(&(qobj), clist, cnum, stcnum);                        \
	}                                                                     \
	static void name##_cmd_clr(void)                                      \
	{                                                                     \
		mqc_cmd_clr(&(qobj));                                             \
	}                                                                     \
	static uint8_t name##_dyna_cmd_req(qc_cmd_type *cmd)                  \
	{                                                                     \
		return mqc_dyna_cmd_req(&(qobj), cmd);                            \
	}                                                                     \
	static uint8_t name##_stc_cmd_req(uint8_t cmd_site, qc_cmd_type *cmd) \
	{                                                                     \
		return mqc_stc_cmd_req(&(qobj), cmd_site, cmd);                   \
	}                                                                     \
	static uint8_t name##_stc_cmd_del(uint8_t cmd_site)                   \
	{                                                                     \
		return mqc_stc_cmd_del(&(qobj), cmd_site);                        \
	}                                                                     \
	static uint8_t name##_cmd_ready_num(void)                             \
	{                                                                     \
		return mqc_cmd_ready_num(&(qobj));                                \
	}                                                                     \
	static uint8_t name##_cmd_idle_num(void)                              \
	{                                                                     \
		return mqc_cmd_idle_num(&(qobj));                                 \
	}                                                                     \
	static const qcom_api_type name##_api = {                             \
		name##_obj_init,                                                  \
		name##_cmd_clr,                                                   \
		name##_dyna_cmd_req,                                              \
		name##_stc_cmd_req,                                               \
		name##_stc_cmd_del,                                               \
		name##_cmd_ready_num,                                             \
		name##_cmd_idle_num}
#define qcom_api_ref(name) extern const qcom_api_type name##_api
#define qcom_api_name(name) &name##_api
#define qcom_api_link(name, qobj) qobj.fun = &name##_api

/*****************************************************************************
 * @brief   hal api
 *****************************************************************************/
// hal api
void qc_data_rcv(qc_obj_type *obj, uu8 dat);
void qc_data08_rcv(qc_obj_type *obj, volatile uu8 *rx_reg, uu8 len);
void qc_data16_rcv(qc_obj_type *obj, volatile uu16 *rx_reg, uu8 len);
void qc_data32_rcv(qc_obj_type *obj, volatile uu32 *rx_reg, uu8 len);
uint8_t qc_data_send(qc_obj_type *obj, void (*send)(uint8_t), uint8_t len);
uint8_t qc_data08_send(qc_obj_type *obj, volatile uint8_t *tx_reg, uint8_t fifo_len);
uint8_t qc_data16_send(qc_obj_type *obj, volatile uint16_t *tx_reg, uint8_t fifo_len);
uint8_t qc_data32_send(qc_obj_type *obj, volatile uint32_t *tx_reg, uint8_t fifo_len);

void qc_send_end(qc_obj_type *obj);

void qc_timer_handle(qc_obj_type *obj);
void qc_package_send(qc_obj_type *obj, uu8 len);

/*****************************************************************************
 * END
 *****************************************************************************/
#endif
